package com.top.web.hooker.search;

import android.os.Handler;
import android.os.Looper;
import android.widget.Filter;
import android.widget.Filterable;
import com.top.web.hooker.common.GroupList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;

public class Filters
{
    public interface KeyProvider
    {
        String[] getKeys();
    }

    public interface GroupFilterLinstener<TKey, TValue>
    {
        void onUpdate();

        void onReady(GroupList<TKey, TValue> list);

        void onCompleted();
    }

    public static class PhoneticGroupFilterable<TKey, TValue> extends GroupFilterable<TKey, TValue>
    {
        private Map<String, String> _pinyin = new HashMap<String, String>();
        private Phonetic _hanziToPinyin = Phonetic.getInstance();
        private Queue<ISearch.Cancelable> _cancelList = new LinkedList<ISearch.Cancelable>();
        private Handler _handler = new Handler(Looper.getMainLooper());

        public PhoneticGroupFilterable(ISearch<TKey, TValue>... array)
        {
            super(array);
        }

        public Object toKey(TValue item)
        {
            if (item == null)
                return "";

            if (item instanceof KeyProvider)
                return item;

            return String.valueOf(item);
        }

        public Phonetic.TokenList getTokenList(String value)
        {
            return _hanziToPinyin.convertToTokens(value);
        }

        public String getFilterString(String key)
        {
            if (_pinyin.containsKey(key))
                return _pinyin.get(key);

            String pinyin = _hanziToPinyin.convertAll(key);

            _pinyin.put(key, pinyin.toLowerCase());

            return pinyin;
        }

        private boolean filterContent(CharSequence constraint, String content)
        {
            String constraintFilter = getFilterString(constraint.toString());

            Phonetic.TokenList list = _hanziToPinyin.convertToTokens(content);

            int index = list.getValue().indexOf(constraintFilter);
            if (index != -1)
            {
                for (int i = 0; i < list.getTokens().size(); i++)
                {
                    if (list.getTokens().get(i).getOffset() == index)
                        return true;
                }
            }

            return false;
        }

        private boolean filterItem(final GroupList<TKey, TValue> filterGroup, CharSequence constraint, final TKey vkey, final TValue value)
        {
            Object key = toKey(value);

            String[] keys;
            if (key instanceof String)
                keys = new String[]{(String) key};
            else
            {
                KeyProvider provider = (KeyProvider) key;
                keys = provider.getKeys();
            }

            for (String k : keys)
            {
                if (filterContent(constraint, k))
                {
                    _handler.post(new Runnable()
                    {
                        @Override
                        public void run()
                        {
                            filterGroup.put(vkey, value);
                        }
                    });

                    return true;
                }
            }

            return false;
        }

        @Override
        public void filter(final CharSequence constraint, ISearch<TKey, TValue>[] searchArray, GroupList<TKey, TValue> out)
        {
            final GroupList<TKey, TValue> filterList = out;

            if (_cancelList.size() > 0)
                for (ISearch.Cancelable c : _cancelList)
                    c.cancel();

            _cancelList.clear();

            for (final ISearch<TKey, TValue> search : searchArray)
            {
                _cancelList.offer(search.search(constraint, new ISearch.SearchListener<TValue>()
                {
                    int _counter = 0;

                    @Override
                    public void onFind(TValue tValue)
                    {
                        if (filterItem(filterList, constraint, search.getName(), tValue))
                        {
                            _counter++;
                            if (_counter <= 20)
                            {
                                getFilterLinstener().onUpdate();
                            }
                        }
                    }

                    @Override
                    public void onCompleted()
                    {
                        getFilterLinstener().onCompleted();
                    }
                }));
            }
        }
    }

    public static abstract class GroupFilterable<TKey, TValue> implements Filterable
    {
        private GroupList<TKey, TValue> _useGroup;
        private ISearch<TKey, TValue>[] _searchArray;
        private Filter _filter;

        public GroupFilterable(ISearch<TKey, TValue>... searchArray)
        {
            _useGroup = new GroupList<>();

            _searchArray = searchArray;
        }

        private GroupFilterLinstener<TKey, TValue> _linstener;

        public void setFilterLinstener(GroupFilterLinstener<TKey, TValue> linstener)
        {
            _linstener = linstener;
        }

        public GroupFilterLinstener<TKey, TValue> getFilterLinstener()
        {
            return _linstener;
        }

        public GroupList<TKey, TValue> getGroupList()
        {
            return _useGroup;
        }

        public abstract void filter(CharSequence constraint, ISearch<TKey, TValue>[] array, GroupList<TKey, TValue> out);

        @Override
        public Filter getFilter()
        {
            if (_filter == null)
                _filter = new Filter()
                {
                    @Override
                    public CharSequence convertResultToString(Object resultValue)
                    {
                        return super.convertResultToString(resultValue);
                    }

                    @Override
                    protected void publishResults(CharSequence constraint, FilterResults results)
                    {
                        _useGroup = (GroupList<TKey, TValue>) results.values;

                        if (_linstener != null)
                            _linstener.onReady(_useGroup);
                    }

                    @Override
                    protected FilterResults performFiltering(final CharSequence constraint)
                    {
                        FilterResults res = new FilterResults();

                        final GroupList<TKey, TValue> filterList = new GroupList<>();

                        GroupFilterable.this.filter(constraint, _searchArray, filterList);

                        res.values = filterList;
                        res.count = 1;//filterList.size();

                        return res;
                    }
                };

            return _filter;
        }

    }
}
